#pragma once

#include "Vector3D.h"
#include "Matrix.h"
#include "Maths.h"

namespace Maths
{
class Quaternion
{
public:
	Quaternion();
	Quaternion( float x, float y, float z, float w );
	Quaternion( float x, float y, float z );
	Quaternion( Vector3D& vector );
	Quaternion( Matrix& matrix );
	bool operator==( const Quaternion& o ) const;
	bool operator!=( const Quaternion& o ) const;
	inline Quaternion& operator=( const Quaternion& o );
	inline Quaternion& operator=( Matrix& o );
	Quaternion operator+( const Quaternion& o ) const;
	Quaternion operator*( const Quaternion& o ) const;
	Quaternion operator*( const float o ) const;
	Quaternion& operator*=( float o );
	Quaternion& operator+=( const Quaternion &b);
	Quaternion& operator-=(const Quaternion &b);
	Vector3D operator*( const Vector3D& vector ) const;
	Quaternion& operator*=( const Quaternion& o );
	float Dot( const Quaternion& o ) const;
	Quaternion& Set( float x, float y, float z, float w );
	Quaternion& Set( float x, float y, float z );
	Quaternion& Set( Vector3D& vector );
	Quaternion& Set( const Quaternion& Quaternion );
	bool Equals( const Quaternion& o, const float tolerance = Maths::EPSILON ) const;
	Quaternion& Normalize();
	Matrix GetMatrix() const;
	void GetMatrix( Matrix& dest, Vector3D& translation ) const;
	void GetTransposedMatrix( Matrix& dest ) const;
	Quaternion& MakeInverse();
	Quaternion Inverse();
	Quaternion& Lerp( Quaternion q1, Quaternion q2, float time );
	Quaternion& Slerp( Quaternion q1, Quaternion q2, float time );
	Quaternion& FromAngleAxis( float angle, Vector3D& axis );
	void ToAngleAxis( float& angle, Vector3D& axis ) const;
	void ToEuler( Vector3D& euler ) const;
	Quaternion& MakeIdentity();
	Quaternion& RotationFromTo(Vector3D& from, Vector3D& to );
	inline float& X()
	{
		return m_X;
	};
	inline float& Y()
	{
		return m_Y;
	};
	inline float& Z()
	{
		return m_Z;
	};
	inline float& W()
	{
		return m_W;
	};

	const static Quaternion Forward, Backward, Left, Right, Up, Down;
private:
	float m_X;
	float m_Y;
	float m_Z;
	float m_W;
};
};